package com.hontech.libratest.utils

import android.os.Environment
import com.hontech.libratest.app.AppTask
import com.hontech.libratest.app.runAsync
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.Calendar
import java.util.Date
import java.util.concurrent.TimeUnit

object Http {

    const val BASE_URL = "http://local.hontech-rdcenter.com:23331/api"

    private val JSON = "application/json;charset=utf8".toMediaType()

    private val client = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .build()

    private var token = ""

    class LoginException(ec: Int, msg: String) : Exception(msg)

    @kotlinx.serialization.Serializable
    data class ErrResp(
        val err_code: Int,
        val err_msg: String
    )

    @kotlinx.serialization.Serializable
    data class LoginReq(
        val account: String,
        val pwd: String
    )

    @kotlinx.serialization.Serializable
    data class User(
        val id: Long,
        val name: String,
        val account: String,
        val pwd_enc: String
    )

    @kotlinx.serialization.Serializable
    data class LoginRes(
        val user: User,
        val token: String
    )

    private fun parseErrResp(resp: Response) {
        val body = resp.body?.string() ?: throw IOException("网络错误")
        val errResp = defJson.decodeFromString<ErrResp>(body)
        throw LoginException(errResp.err_code, errResp.err_msg)
    }

    private fun loginIfNotToken() {
        val login = LoginReq("device", "device1234")
        val reqBody = defJson.encodeToString(login)
        val req = Request.Builder()
            .post(reqBody.toRequestBody(JSON))
            .url("$BASE_URL/user/login")
            .build()
        val call = client.newCall(req)
        val resp = call.execute()
        if (resp.code != 200) {
            throw IOException("http响应错误:${resp.code}")
        }
        val head = resp.headers["resp"] ?: throw IOException("数据异常")
        if (head != "ok") {
            parseErrResp(resp)
        }
        val body = resp.body?.string() ?: throw IOException("网络错误")
        val res = defJson.decodeFromString<LoginRes>(body)
        token = res.token
        log("登录成功 $res")
    }

    private fun getResp(url: String): Response {
        val req = Request.Builder()
            .get()
            .url("$BASE_URL${url}")
            .addHeader("token", token)
            .build()
        val call = client.newCall(req)
        val resp = call.execute()
        if (resp.code != 200) {
            throw IOException("http响应错误:${resp.code}")
        }
        val head = resp.headers["resp"] ?: throw IOException("数据异常")
        if (head == "fail") {
            parseErrResp(resp)
        }
        return resp
    }

    private fun doGetResp(url: String): Response {

        val resp = try {
            getResp(url)
        } catch (e: LoginException) {
            e.printStackTrace()
            loginIfNotToken()
            getResp(url)
        }

        return resp
    }

    private fun doGet(url: String): ByteArray {
        val resp = doGetResp(url)
        return resp.body?.bytes() ?: throw IOException("网络错误")
    }

    suspend fun doDownloadApk(id: Int, cb: (Long) -> Unit): String {

        return runAsync {
            val url = "/dev_file/get?id=${id}"
            val file = File(Environment.getExternalStorageDirectory(), "client.apk")
            if (!file.exists()) {
                file.createNewFile()
            }
            val out = FileOutputStream(file)
            val body = doGetResp(url).body ?: throw IOException("网络错误")
            val size = body.contentLength()
            val input = body.byteStream()
            input.use {
                out.use {
                    val buf = ByteArray(1024 * 10)
                    var index = 0L
                    while (index < size) {

                        val n = input.read(buf)
                        if (n <= 0) {
                            throw IOException("recv fail")
                        }
                        out.write(buf, 0, n)
                        index += n
                        AppTask.post { cb(index * 100 / size) }
                    }
                }
            }
            file.path
        }
    }

    suspend fun doDownloadBin(id: Int): ByteArray {
        val url = "/dev_file/get?id=${id}"
        return runAsync { doGet(url) }
    }

    @kotlinx.serialization.Serializable
    data class StoreFile(
        val id: Int,
        val description: String,
        val version: String,
        val size: Int,
        val create_timestamp: Long
    ) {

        fun time(): String {
            val calendar = Calendar.getInstance()
            calendar.time = Date(create_timestamp * 1000)
            val year = calendar.get(Calendar.YEAR)
            val month = calendar.get(Calendar.MONTH) + 1
            val day = calendar.get(Calendar.DAY_OF_MONTH)
            val hour = calendar.get(Calendar.HOUR_OF_DAY)
            val minute = calendar.get(Calendar.MINUTE)
            val sec = calendar.get(Calendar.SECOND)
            return "$year-$month-$day $hour:$minute:$sec"
        }
    }

    suspend fun queryMcu(): Array<StoreFile> {
        val body = runAsync {
            doGet("/dev_file/query?type_id=19")
        }
        return defJson.decodeFromString(String(body))
    }

    suspend fun queryApkFile(): Array<StoreFile> {
        val body = runAsync {
            doGet("/dev_file/query?type_id=20")
        }
        return defJson.decodeFromString(String(body))
    }
}

